#include "protocol_transport.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <stdlib.h>
#include <string.h>
#include <esp_crt_bundle.h>
#include <esp_log.h>
#include <bsp.h>
#include "esp_websocket_client.h"

#define TAG "[PROTOCOL] Websocket"
#define WEBSOCKET_CONNECTED_BIT (1 << 0)

#define WEBSOCKET_URL "wss://api.tenclass.net/xiaozhi/v1/"
#define WEBSOCKET_TOKEN "test-token"

typedef struct protocol_transport_websocket
{
    protocol_transport_t transport;
    protocol_transport_callbacks_t *callbacks;
    void *protocol;
    EventGroupHandle_t event_group;
    esp_websocket_client_handle_t client;
} protocol_transport_websocket_t;

// 处理回调
static void protocol_transport_websocket_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    esp_websocket_event_data_t *data = (esp_websocket_event_data_t *)event_data;
    protocol_transport_websocket_t *transport = (protocol_transport_websocket_t *)handler_args;
    switch (event_id)
    {
    case WEBSOCKET_EVENT_BEGIN:
        ESP_LOGI(TAG, "WEBSOCKET_EVENT_BEGIN");
        break;
    case WEBSOCKET_EVENT_CONNECTED:
        ESP_LOGI(TAG, "WEBSOCKET_EVENT_CONNECTED");
        xEventGroupSetBits(transport->event_group, WEBSOCKET_CONNECTED_BIT);
        break;
    case WEBSOCKET_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "WEBSOCKET_EVENT_DISCONNECTED");
        break;
    case WEBSOCKET_EVENT_DATA:
        ESP_LOGD(TAG, "WEBSOCKET_EVENT_DATA");
        if (data->op_code == 0x01)
        {
            // 文本数据
            if (transport->callbacks->text_callback)
            {
                char *ptr = data->data_ptr;
                ptr[data->data_len] = 0;
                ESP_LOGI(TAG, "received text: %s", ptr);
                transport->callbacks->text_callback(transport->protocol, ptr);
            }
        }
        else if (data->op_code == 0x02)
        {
            // 音频数据
            if (transport->callbacks->audio_callback)
            {
                transport->callbacks->audio_callback(transport->protocol, (void *)data->data_ptr, data->data_len);
            }
        }

        break;
    case WEBSOCKET_EVENT_ERROR:
        ESP_LOGI(TAG, "WEBSOCKET_EVENT_ERROR");
        break;
    case WEBSOCKET_EVENT_FINISH:
        ESP_LOGI(TAG, "WEBSOCKET_EVENT_FINISH");
        if (transport->callbacks->channel_close_callback)
        {
            transport->callbacks->channel_close_callback(transport->protocol);
        }
        break;
    }
}

// 开，关，查询状态
static bool protocol_transport_websocket_open_audio_channel(protocol_transport_t *transport)
{
    protocol_transport_websocket_t *ws_transport = (protocol_transport_websocket_t *)transport;
    // 开启websocket音频通道
    esp_websocket_client_config_t websocket_config = {
        .uri = WEBSOCKET_URL,
        .buffer_size = 2048,
        .transport = WEBSOCKET_TRANSPORT_OVER_SSL,
        .crt_bundle_attach = esp_crt_bundle_attach,
        .network_timeout_ms = 5000,
        .reconnect_timeout_ms = 5000,
    };

    ws_transport->client = esp_websocket_client_init(&websocket_config);
    if (!ws_transport->client)
    {
        return false;
    }

    ESP_ERROR_CHECK(esp_websocket_register_events(ws_transport->client, WEBSOCKET_EVENT_ANY, protocol_transport_websocket_event_handler, (void *)ws_transport));

    esp_websocket_client_append_header(ws_transport->client, "Authorization", "Bearer " WEBSOCKET_TOKEN);
    esp_websocket_client_append_header(ws_transport->client, "Protocol-Version", "1");
    esp_websocket_client_append_header(ws_transport->client, "Device-Id", bsp_get_mac_address());
    esp_websocket_client_append_header(ws_transport->client, "Client-Id", bsp_get_uuid());

    if (esp_websocket_client_start(ws_transport->client) != ESP_OK)
    {
        ESP_LOGE(TAG, "Failed to connect to websocket server");
        goto DESTROY_FAIL;
    }

    xEventGroupWaitBits(ws_transport->event_group, WEBSOCKET_CONNECTED_BIT, pdTRUE, pdTRUE, portMAX_DELAY);

    return true;

DESTROY_FAIL:
    esp_websocket_client_destroy(ws_transport->client);
    ws_transport->client = NULL;
    return false;
}

static void protocol_transport_websocket_close_audio_channel(protocol_transport_t *transport)
{
    protocol_transport_websocket_t *ws_transport = (protocol_transport_websocket_t *)transport;
    if (esp_websocket_client_is_connected(ws_transport->client))
    {
        esp_websocket_client_close(ws_transport->client, portMAX_DELAY);
    }
    esp_websocket_client_destroy(ws_transport->client);
    ws_transport->client = NULL;
}

static bool protocol_transport_websocket_is_audio_channel_opened(protocol_transport_t *transport)
{
    protocol_transport_websocket_t *ws_transport = (protocol_transport_websocket_t *)transport;
    return esp_websocket_client_is_connected(ws_transport->client);
}

// 发送二进制和文本
static void protocol_transport_websocket_send_audio(protocol_transport_t *transport, void *data, size_t len)
{
    protocol_transport_websocket_t *ws_transport = (protocol_transport_websocket_t *)transport;

    if (esp_websocket_client_is_connected(ws_transport->client))
    {
        assert(esp_websocket_client_send_bin(ws_transport->client, data, len, portMAX_DELAY) > 0);
    }
    else
    {
        ESP_LOGE(TAG, "Websocket not connected");
    }
}

static void protocol_transport_websocket_send_text(protocol_transport_t *transport, char *text)
{
    protocol_transport_websocket_t *ws_transport = (protocol_transport_websocket_t *)transport;
    if (esp_websocket_client_is_connected(ws_transport->client))
    {
        ESP_LOGI(TAG, "Sending text %s", text);
        assert(esp_websocket_client_send_text(ws_transport->client, text, strlen(text), portMAX_DELAY) > 0);
    }
    else
    {
        ESP_LOGE(TAG, "Websocket not connected");
    }
}

// 清理
static void protocol_transport_websocket_deinit(protocol_transport_t *transport)
{
    protocol_transport_websocket_t *ws_transport = (protocol_transport_websocket_t *)transport;
    if (ws_transport->client)
    {
        protocol_transport_websocket_close_audio_channel(transport);
    }
    vEventGroupDelete(ws_transport->event_group);
    free(ws_transport);
}

// 初始化
protocol_transport_t *protocol_transport_create_websocket(protocol_transport_callbacks_t *callbacks, void *protocol)
{
    protocol_transport_websocket_t *transport = malloc(sizeof(protocol_transport_websocket_t));
    assert(transport);
    memset(transport, 0, sizeof(protocol_transport_websocket_t));

    transport->transport.type = PROTOCOL_TRANSPORT_TYPE_WEBSOCKET,
    transport->transport.start = NULL,
    transport->transport.open_audio_channel = protocol_transport_websocket_open_audio_channel,
    transport->transport.close_audio_channel = protocol_transport_websocket_close_audio_channel,
    transport->transport.is_audio_channel_open = protocol_transport_websocket_is_audio_channel_opened,
    transport->transport.send_audio = protocol_transport_websocket_send_audio,
    transport->transport.send_text = protocol_transport_websocket_send_text,
    transport->transport.deinit = protocol_transport_websocket_deinit,

    transport->protocol = protocol;
    transport->callbacks = callbacks;
    transport->event_group = xEventGroupCreate();
    return (protocol_transport_t *)transport;
}
